Jx.categorize('lang.css.selector.compiler', {

	$extend: 'Jx.code.Compiler',

	$actions: [

		'filter',

		'is',

		'query'

	],

	parser: 'lang.css.selector.parser',

	compiled_is: undefined,

	trace: true,

	pseudoHandlerPrefix: '__handler_pseudo_',

	'__parse_Query': function(operation, lexeme, len, name){

		// filter
		var params = operation.params,

			pl = params.length,

			codes = operation.codes,

			cl = codes.length;

		params[pl++] = 'operation';
		params[pl++] = 'subject';



	},

	'__parse_Group': function(operation, lexeme, count){



	},

	'__parse_Selector': function(operation, lexeme, count, selectorLex, combinatorLex, simpleLex){

		// selector, combinator and simple_selector
		if (count == 3){

			var method = 'createSelectorCode[' + combinatorLex.value + ']';

			if (method in this){

				this.applyVariableIf(

					lexeme,

					operation,

					this[ method ]( operation, lexeme, selectorLex, simpleLex )

				);

				//-- compile!
				this.registerCompiledIf(

					lexeme.getCode(),

					operation.getSymbolValue(lexeme.ref, "expression"),

					['operation', 'subject']

				);


			}

		// simple_selector, do nothing!

		}

	},

	'__parse_Simple_Selector': function(operation, lexeme, count, typeLex, attrsLex){

		if (typeLex.production == 'Type_Attribute'){

			attrsLex = typeLex;

			typeLex = void(0);

		}

		var mainCodes = [],

			enclosures = [],

			variables = [],

			mc = 0,

			ec = 0,

			vc = 0,

			refs, ref, len, before, after, symbol;


		// type_selector
		if (typeLex &&

			(ref = typeLex.ref)

		){

			variables[ vc++ ] = ref;

			mainCodes[ mc++ ] = operation.getSymbolValue(ref, 'expression') + ';';

			mainCodes[ mc++ ] = 'if ('+ref+'){ ';

			enclosures[ ec++ ] = '}';


		}

		// type_attribute
		if (attrsLex &&

			(len = (refs = attrsLex.refs).length)

		){

			refs = operation.sortSymbols(refs);

			var created = {};

			for(var code, c=-1, l=len; l--;){

				ref = refs[++c];

				code = operation.getSymbolValue(ref, 'expression') + ';';

				if (!(code in created)){

					created[code] = true;

					variables[ vc++ ] = ref;

					mainCodes[ mc++ ] = code;

					mainCodes[ mc++ ] = 'if ('+ref+'){ ';

					enclosures[ ec++ ] = '}';

				}

			}

		}

		// register compiled
		if (mc){

			before = mainCodes.join("\r\n");

			after = enclosures.join("\r\n");

			mainCodes[ mc++ ] = 'return false;';

			mainCodes[ mc++ ] = enclosures.join('');

			ref = lexeme.ref = operation.createSymbol(

				[
					'var Mgr = operator.Mgr, indexedNodes = operator.indexed;',

					'var '+operation.joinSymbols(variables, ",", "name")+';',

					before,

					'return true;',

					after,

					'return false;'

				].join("\r\n")

			);

			// update symbol properties
			symbol = operation.getSymbol(ref);

			symbol.variables = variables;

			symbol.header = before;

			symbol.footer = after;

			// compile simple selector
			this.registerCompiledIf(

				lexeme.getCode(),

				operation.getSymbolValue(ref, "expression"),

				['operation', 'subject']

			);

		}

	},

	'__parse_Type_Selector': function(operation, lexeme, count, lns, lpipe, lname){

		var name = lns.value,

			ns = '*',

			declaration;

		// | name
		if (count == 2){

			name = lpipe.value;

		// ns | name
		} else if (count == 3){

			ns = lns.value;

			name = lname.value;

		}

		lexeme.vals = [ ns, name ];

		this.applyVariableIf(

			lexeme,

			operation,

			this.createTypeSelectorCode(ns, name)

		);

	},

	'__parse_Type_Attribute': function(operation, lexeme, count, main, extra){

		var refs = lexeme.refs = [],

			ref;

		if (count == 2){

			var items = main.refs;

			if (items.length){

				items = main.refs;

				refs.push.apply(refs, items.splice(0, items.length));

			}

			main = extra;

		}

		if (main.ref){

			refs[refs.length] = main.ref;

		}


	},

	'__parse_Attribute': function(operation, lexeme, count, oB, nameLex, opLex, valLex, cB){

		var vals = lexeme.vals = [],

			i = 0,

			name = nameLex.value,

			operator = 'has',

			value = '',

			method;

		vals[i++] = name;

		// created 'value' and 'operator' sequence
		if (count > 3){

			vals[i++] = operator = opLex.value;

			vals[i++] = value = valLex.value;

		}

		method = 'createAttributeCode[' + operator + ']';

		if (method in this){

			this.applyVariableIf(

				lexeme,

				operation,

				this[ method ]( name, value )

			);

		}

	},

	'__parse_Hash': function(operation, lexeme, count, hashOp, hashLex){

		var value = hashLex.value,

			method = 'createAttributeCode[=]';

		lexeme.vals = [ value ];

		if (method in this){

			this.applyVariableIf(

				lexeme,

				operation,

				this[method]( 'id', value)

			);

		}

	},

	'__parse_Class': function(operation, lexeme, count, classOp, classLex){

		var value = classLex.value,

			method = 'createAttributeCode[~=]';

		lexeme.vals = [ value ];

		if (method in this){

			this.applyVariableIf(

				lexeme,

				operation,

				this[method]('class', value)

			);

		}

	},

	'__parse_Pseudo': function(operation, lexeme, count, pseudoOp, pseudoName, pseudoStart, pseudoValue, pseudoEnd){

		var name = psuedoName.value,

			method = '__parse_Pseudo_' + psuedoOp.value + name;

		lexeme.vals = [];

		// a separate handler for parsing pseudo by name only if method exist '__parse_Pseudo_' + name
		if (method in this){

			this.applyVariableIf(

				lexeme,

				operation,

				this[method](name, psuedoValue.getCode())

			);

		}

	},

	__helper_ApplySimpleSelectorCode: function(operation, lexeme, codes){

		var c = codes.length,

			simpleSymbol = operation.getSymbol(simpleLex.ref);

		if (simpleSymbol){

			codes[c++] = 'var '+operation.joinSymbols(simpleSymbol.variables, ",", "name")+';';

			codes[c++] = simpleSymbol.header;

			codes[c++] = 'rValue = true;';

			codes[c++] = simpleSymbol.footer;

			codes[c++] = 'if (!rValue){ return false; }';

		}

	},

	constructor: function(){

		this.compiled_hinted_is = {};

		this.$super(arguments);

	},

	onPurge: function(){

		Jx.clearObject(this.compiled_is);

		this.$super(arguments);

	},

	applyVariableIf: function(lexeme, operation, declaration, order){

		if (declaration){

			lexeme.ref = operation.createSymbol({

				assign: true,

				returns: true,

				expression: declaration,

				order: order || 0

			});

		}

	},

	createSetQueryIdCode: function(nodeVariable, idVariable){

		return nodeVariable+'.setAttribute("__query_id", '+idVariable+');';

	},

	createGetQueryIdCode: function(nodeVariable, idVariable){

		return idVariable+' = '+nodeVariable+'.getAttribute("__query_id");';

	},

	createTypeSelectorCode: function(ns, name){

		var codes = [],

			S = this.__Str,

			c = 0;

		// this is only valid for @namespace css rule
		//if (ns != '*'){
		//
		//	codes[c++] = 'subject.namespaceURI == "' + S.escape(ns) + '"';
		//
		//}

		if (name != '*'){

			codes[c++] = 'subject.nodeName == "' + S.escape(name) + '"';

		}

		return codes.join(' && ');

	},

	createGetParentNodeCode: function(nodeVariable){

		return nodeVariable+'.parentNode';

	},

	createGetFirstChildNodeCode: function(nodeVariable){

		return nodeVariable+'.firstChild';

	},

	createGetLastChildCode: function(nodeVariable){

		return nodeVariable+'.lastChild';

	},

	createGetPreviousSiblingCode: function(nodeVariable){

		return nodeVariable+'.previousSibling';

	},

	createGetNextSiblingCode: function(nodeVariable){

		return nodeVariable+'.nextSibling';

	},

	createIsValidNodeCode: function(nodeVariable){

		return 'true'; // nodeVariable+'.nodeType=1'; // will be used for XML documents

	},

	createIterateParentNodeCode: function(nodeVariable, pointerVariable){

		return [

			// header
			[

				// iterate expression
				[

					'for(var ',

					pointerVariable,' = ',this.createGetParentNodeCode(nodeVariable),

					'; ', pointerVariable,

					'; ', pointerVariable, '=', this.createGetParentNodeCode(pointerVariable),

					')'

				].join(''),

				'{'

			].join("\n"),

			// footer
			[

				'}'

			].join("\n"),

		];


	},

	createIterateChildNodesCode: function(nodeVariable, pointerVariable){

		return [

			// header
			[

				// iterate expression
				 [

					'for(var ',

					pointerVariable,' = ',this.createGetFirstChildNodeCode(nodeVariable),

					'; ', pointerVariable,

					'; ', pointerVariable, '=', this.createGetNextSiblingCode(pointerVariable),

					')'

				].join(''),

				'{',

				//-- filter only valid nodes

				[
					'if (!',

					this.createIsValidNodeCode(nodeVariable),

					'){ continue; }'

				].join("\n")

			].join("\n"),

			// footer
			[

				'}'

			].join("\n"),

		];

	},

	createIteratePreviousSiblingCode: function(nodeVariable, pointerVariable){

		return [

			// header
			[

				// iterate expression
				 [

					'for(var ',

					pointerVariable,' = ',this.createGetPreviousSiblingCode(nodeVariable),

					'; ', pointerVariable,

					'; ', pointerVariable, '=', this.createGetPreviousSiblingCode(pointerVariable),

					')'

				].join(''),

				'{',

				//-- filter only valid nodes

				[
					'if (!',

					this.createIsValidNodeCode(nodeVariable),

					'){ continue; }'

				].join("\n")

			].join("\n"),

			// footer
			[

				'}'

			].join("\n"),

		];

	},

/**
 *	Attribute Comparison
 */

	// subject element with a "name" attribute
	'createAttributeCode[has]': function(name){

		var qname = '"' + this.__Str.escape(name) + '"';

		return 'J.isPropertyEnumerable(' +qname+ ', subject) && !!subject['+qname+']';

	},

	// subject element whose "name" attribute value is exactly equal to "value"
	'createAttributeCode[=]': function(name, value){

		var S = this.__Str,

			qname = '"' + S.escape(name) + '"';

		return qname + ' in subject && subject['+qname+'] == "' + S.escape(value) + '"';

	},

	// subject element whose "name" attribute value contains the substring "value"
	'createAttributeCode[*=]': function(name, value){

		var S = this.__Str,

			qname = '"' + S.escape(name) + '"';

		return qname + ' in subject && subject['+qname+'] == "' + S.escape(value) + '"';

	},

/**
 * Selector Combinator Simple_Selector
 */

	// simpleLex element descendant of a selectorLex element
	'createSelectorCode[ ]': function(operation, lexeme, selectorLex, simpleLex){

		var parts = this.createIterateParentNodeCode('subject', 'p'),

			codes = [],

			c = 0;

		codes[c++] = 'var rValue = false, Mgr = operator.Mgr, indexedNodes = operator.indexed;';

		//-- simple selector code
		this.__helper_ApplySimpleSelectorCode(operator, simpleLex, codes);

		//-- iterate parent
		codes[c++] = 'var isSelector=Mgr.compiled["' + this.__Str.escape( selectorLex.getCode() ) + '"];';

		codes[c++] = parts[0];

		codes[c++] = 'if (isSelector(operation, p)){ return true; }';

		codes[c++] = parts[1];

		codes[c++] = 'return false;';

		return codes.join("\n");

	},

	// simpleLex element child of a selectorLex element
	'createSelectorCode[>]': function(operation, lexeme, selectorLex, simpleLex){

		var codes = [],

			c = 0;

		codes[c++] = 'var rValue = false, Mgr = operator.Mgr, indexedNodes = operator.indexed;';

		//-- simple selector code
		this.__helper_ApplySimpleSelectorCode(operator, simpleLex, codes);

		//-- get parentNode
		codes[c++] = 'var parentNode = '+this.createGetParentNodeCode('subject')+';';

		codes[c++] = 'return !!parentNode && Mgr.compiled["' + this.__Str.escape( selectorLex.getCode() ) + '"](operation, parentNode);';

		return codes.join("\n");

	},

	// simpleLex element immediately preceded by a selectorLex  element
	'createSelectorCode[+]': function(operation, lexeme, selectorLex, simpleLex){

		var parts = this.createIteratePreviousSiblingCode('subject', 'sibling'),

			codes = [],

			c = 0;

		codes[c++] = 'var rValue = false, Mgr = operator.Mgr, indexedNodes = operator.indexed;';

		//-- simple selector code
		this.__helper_ApplySimpleSelectorCode(operator, simpleLex, codes);

		//-- get parentNode
		codes[c++] = parts[0];

		codes[c++] = 'return Mgr.compiled["' + this.__Str.escape( selectorLex.getCode() ) + '"](operation, sibling);';

		codes[c++] = parts[1];

		codes[c++] = 'return false;';

		return codes.join("\n");

	},

	// simpleLex element preceded by a selectorLex  element
	'createSelectorCode[~]': function(operation, lexeme, selectorLex, simpleLex){

		var parts = this.createIteratePreviousSiblingCode('subject', 'sibling'),

			codes = [],

			c = 0;

		codes[c++] = 'var rValue = false, Mgr = operator.Mgr, indexedNodes = operator.indexed;';

		//-- simple selector code
		this.__helper_ApplySimpleSelectorCode(operator, simpleLex, codes);

		//-- get parentNode
		codes[c++] = 'var isSelector = Mgr.compiled["' + this.__Str.escape( selectorLex.getCode() ) + '"];';

		codes[c++] = parts[0];

		codes[c++] = 'if (isSelector(operation, sibling)){ return true; }';

		codes[c++] = parts[1];

		codes[c++] = 'return false;';

		return codes.join("\n");

	}

});
